/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package com.googlecode.ivybeans.module;

import com.googlecode.ivybeans.module.util.FileHelper;
import com.googlecode.ivybeans.module.classpath.ScopeConfigurationMapping;
import com.googlecode.ivybeans.facade.ResolvedIvyLibrary;
import com.googlecode.ivybeans.module.classpath.ClassPathScope;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.netbeans.api.project.Project;
import org.netbeans.spi.project.AuxiliaryConfiguration;
import org.netbeans.spi.project.support.ant.AntProjectHelper;
import org.openide.util.EditableProperties;
import org.openide.filesystems.FileObject;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * 
 * @author lforet
 */
public class ProjectConfigurationHelper {

    public static final String PROJECT_CONFIGURATION_NAMESPACE = "http://code.google.com/p/ivybeans/ns/ivy-module/1"; // NOI18N
    public static final String IVY_CONFIGURATION_ELEMENT_NAME = "ivyConfiguration"; //NO I18N
    public static final String DESCRIPTOR_FILE_ELEMENT_NAME = "descriptorFile"; //NO I18N
    public static final String SETTINGS_FILE_ELEMENT_NAME = "settingsFile"; //NO I18N
    public static final String CONFIGURATION_MAPPING_ELEMENT_NAME = "configurationMapping"; //NO I18N
    public static final String CLASSPATH_SCOPE_ELEMENT_NAME = "scope"; //NO I18N
    public static final String CLASSPATH_SCOPE_NAME_ATTR = "name"; //NO I18N
    public static final String CLASSPATH_SCOPE_CONFS_ATTR = "confs"; //NO I18N
    public final static String CONF_SEPARATOR = ",";
    public static final String SETTINGS_LOCATION_PROPERTY_NAME = "ivy.settings.location"; // NOI18N
    public static final String SETTINGS_LOCATION_KIND_PROPERTY_NAME = "ivy.settings.location.kind"; // NOI18N
    public static final String DESCRIPTOR_FILE_PROPERTY_NAME = "ivy.descriptor.file"; // NOI18N
    private static ProjectConfigurationHelper instance;

    private ProjectConfigurationHelper() {
    }

    public static ProjectConfigurationHelper getInstance() {
        if (instance == null) {
            instance = new ProjectConfigurationHelper();
        }
        return instance;
    }

    public IvyLibraryController initController(Project project, AuxiliaryConfiguration auxiliaryConfiguration) {
        boolean enabled = false;
        String descriptorFilename = null;
        String settingsFilename = null;
        ScopeConfigurationMapping configurationMapping = null;

        Element ivyConfElt = auxiliaryConfiguration.getConfigurationFragment(IVY_CONFIGURATION_ELEMENT_NAME, PROJECT_CONFIGURATION_NAMESPACE, true);
        if (ivyConfElt != null) {
            enabled = true;
            EditableProperties props = loadProjectProperties(project);
            if(props.containsKey(DESCRIPTOR_FILE_PROPERTY_NAME)) {
                descriptorFilename = props.get(DESCRIPTOR_FILE_PROPERTY_NAME);
            } else {
                // for migration purposes - load it from an old project.xml that has it stored there
                Element descriptorFileElt = (Element) ivyConfElt.getElementsByTagName(DESCRIPTOR_FILE_ELEMENT_NAME).item(0);
                if (descriptorFileElt != null) {
                    descriptorFilename = descriptorFileElt.getTextContent();
                }
            }

            if(props.containsKey(SETTINGS_LOCATION_PROPERTY_NAME)) {
                settingsFilename = props.get(SETTINGS_LOCATION_PROPERTY_NAME);
            } else {
                // for migration purposes - load it from an old project.xml fthat has it stored there
                Element settingsFileElt = (Element) ivyConfElt.getElementsByTagName(SETTINGS_FILE_ELEMENT_NAME).item(0);
                if (settingsFileElt != null) {
                    settingsFilename = settingsFileElt.getTextContent();
                }
            }
            Element configurationMappingElement = (Element) ivyConfElt.getElementsByTagName(CONFIGURATION_MAPPING_ELEMENT_NAME).item(0);
            if (configurationMappingElement != null) {
                NodeList scopeElements = configurationMappingElement.getElementsByTagName(CLASSPATH_SCOPE_ELEMENT_NAME);
                configurationMapping = new ScopeConfigurationMapping();
                for (int i = 0; i < scopeElements.getLength(); i++) {
                    Element currentScopeElement = (Element) scopeElements.item(i);
                    ClassPathScope cpScope = ClassPathScope.valueOf(currentScopeElement.getAttribute(CLASSPATH_SCOPE_NAME_ATTR));
                    String[] confs = tokenize(currentScopeElement.getAttribute(CLASSPATH_SCOPE_CONFS_ATTR));
                    configurationMapping.put(cpScope, confs, ResolvedIvyLibrary.EMPTY_RESOLVED);
                }
            }
        }
        return new IvyLibraryController(project, enabled, descriptorFilename, settingsFilename, configurationMapping);
    }

    public void clean(Project project) {
        AuxiliaryConfiguration auxiliaryConfiguration = project.getLookup().lookup(AuxiliaryConfiguration.class);
        assert auxiliaryConfiguration != null;
        auxiliaryConfiguration.removeConfigurationFragment(IVY_CONFIGURATION_ELEMENT_NAME, PROJECT_CONFIGURATION_NAMESPACE, true);
    }

    private EditableProperties loadProjectProperties(Project project) {
        final FileObject projPropsFile = project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
        final EditableProperties props = new EditableProperties(false);
        InputStream inputStream = null;
        try {
            inputStream = projPropsFile.getInputStream();
            props.load(inputStream);
        } catch (IOException ignore) {
        } finally {
            try {
                inputStream.close();
            } catch (IOException ignore) {
            }
        }
        return props;
    }

    public void store(Project project) {
        clean(project);
        IvyLibraryController ivyLibraryController = project.getLookup().lookup(IvyLibraryController.class);
        if (ivyLibraryController.isIvyEnabled()) {
            AuxiliaryConfiguration auxiliaryConfiguration = project.getLookup().lookup(AuxiliaryConfiguration.class);
            Document doc = XMLUtil.createDocument("whatever", null, null, null);
            Element fragment = doc.createElementNS(PROJECT_CONFIGURATION_NAMESPACE, IVY_CONFIGURATION_ELEMENT_NAME);
            EditableProperties props = loadProjectProperties(project);
            
            File descriptorFile = ivyLibraryController.getDescriptorFile();
            if (descriptorFile != null) {
                String descriptorPath = FileHelper.getInstance().getPath(project.getProjectDirectory(), descriptorFile);
                props.setProperty( DESCRIPTOR_FILE_PROPERTY_NAME, descriptorPath);
            }
            File settingsFile = ivyLibraryController.getSettingsFile();
            URL settingsUrl = ivyLibraryController.getSettingsURL();
            if (settingsFile != null) {
                String settingsPath = FileHelper.getInstance().getPath(project.getProjectDirectory(), settingsFile);
                props.setProperty(SETTINGS_LOCATION_KIND_PROPERTY_NAME, "file");
                props.setProperty(SETTINGS_LOCATION_PROPERTY_NAME, settingsPath);
            } else if (settingsUrl != null) {
                props.setProperty(SETTINGS_LOCATION_KIND_PROPERTY_NAME, "url");
                props.setProperty(SETTINGS_LOCATION_PROPERTY_NAME, settingsUrl.toString());
            }
            if(props.containsKey(SETTINGS_LOCATION_KIND_PROPERTY_NAME)) {
                props.setComment(SETTINGS_LOCATION_KIND_PROPERTY_NAME,
                    new String[] {
                        "# whether " + SETTINGS_LOCATION_PROPERTY_NAME + " is to be interpreted as a file or url." ,
                        "# valid values are 'url' and 'file'."
                    },
                    false);
            }
            try {
                final FileObject projPropsFile = project.getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
                final OutputStream outputStream = projPropsFile.getOutputStream();
                props.store(outputStream);
                outputStream.close();
            } catch (IOException iOException) {
                iOException.printStackTrace();
            }
            Element configurationMappingElement = doc.createElement(CONFIGURATION_MAPPING_ELEMENT_NAME);
            fragment.appendChild(configurationMappingElement);
            for (ClassPathScope scope : ClassPathScope.values()) {
                Element scopeElement = doc.createElement(CLASSPATH_SCOPE_ELEMENT_NAME);
                scopeElement.setAttribute(CLASSPATH_SCOPE_NAME_ATTR, scope.name());
                String confs = untokenize(ivyLibraryController.getConfigurationMapping().getConfiguration(scope));
                scopeElement.setAttribute(CLASSPATH_SCOPE_CONFS_ATTR, confs);
                configurationMappingElement.appendChild(scopeElement);
            }
            auxiliaryConfiguration.putConfigurationFragment(fragment, true);
        }
    }

    public String[] tokenize(String confs) {
        StringTokenizer tokenizer = new StringTokenizer(confs, CONF_SEPARATOR);
        List<String> tokens = new ArrayList<String>();
        while (tokenizer.hasMoreTokens()) {
            tokens.add(tokenizer.nextToken().trim());
        }
        return tokens.toArray(new String[0]);
    }
    
    public String untokenize(String[] tokens) {
        String toReturn = "";
        if (tokens == null || tokens.length == 0) {
            return toReturn;
        }
        for (int i = 0; i < tokens.length -1; i++ ) {
            toReturn += tokens[i]+ CONF_SEPARATOR;
        }
        toReturn += tokens[tokens.length - 1];
        return toReturn;
    }
}
